import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * 顾客接口，能吃，能睡，能玩。
 */
interface Customer {

    void eat();

    void sleep();

    void play();
}

/**
 * 普通顾客，能吃，能睡，能玩。
 */
class OrdinaryCustomer implements Customer {

    @Override
    public void eat() {
        System.out.println("吃68的普通菜!");
    }

    @Override
    public void sleep() {
        System.out.println("睡标间!");
    }

    @Override
    public void play() {
        System.out.println("泡公共浴池!");
    }
}

public class DynamicProxy {

    public static void main(String[] args) {
        // 普通顾客的功能
        Customer ordinaryCustomer = new OrdinaryCustomer();
        ordinaryCustomer.eat();
        ordinaryCustomer.sleep();
        ordinaryCustomer.play();

        System.out.println("--------------VIP-----------------");

        // 现在有一位特殊的顾客，他是老板的朋友，我们自然要对他额外照顾，要给他加一下功能。
        // 一种思路是定义一个类：老板的朋友类，他继承自顾客类，但世上VIP太多了，有老板的朋友，也有总经理的朋友，不能把他们挨个定义出来，
        // 现在就可以使用动态代理机制，动态的给老板的朋友加上功能，以后碰到总经理的朋友也同样处理。

        // InvocationHandler作用就是，当代理对象的原本方法被调用的时候，会重定向到一个方法，
        // 这个方法就是InvocationHandler里面定义的内容，同时会替代原本方法的结果返回。
        // InvocationHandler接收三个参数：proxy，代理后的实例对象。 method，对象被调用方法。args，调用时的参数。

        InvocationHandler handler = (proxy, method, handlerArgs) -> {
            // 重定义eat方法。
            if ("eat".equals(method.getName())) {
                System.out.println("我可以吃688的VIP山珍海味！");
                return null;
            }
            // 重定义sleep方法。
            if ("sleep".equals(method.getName())) {
                System.out.println("我是老板的朋友!");
                // 调用普通顾客类的sleep方法.
                method.invoke(ordinaryCustomer, handlerArgs);
                System.out.println("但是是总统级标间!");
                return null;
            }

            // 重定义play方法。
            if ("play".equals(method.getName())) {

                System.out.println("泡私人温泉!");
                return null;
            }

            return null;
        };

        // 对这个实例对象代理生成一个代理对象。
        // 被代理后生成的对象，是通过Customer接口的字节码增强方式创建的类而构造出来的。它是一个临时构造的实现类的对象。
        // loader和interfaces基本就是决定了这个类到底是个怎么样的类。而h是InvocationHandler，决定了这个代理类到底是多了什么功能.
        // 通过这些接口和类加载器，拿到这个被代理类class。然后通过反射的技术复制拿到代理类的构造函数，
        // 最后通过这个构造函数new个一对象出来，同时用InvocationHandler绑定这个对象。
        // 最终实现可以在运行的时候才切入改变类的方法，而不需要预先定义它。
        Customer friendOfBoss = (Customer) Proxy.newProxyInstance(ordinaryCustomer.getClass().getClassLoader(), ordinaryCustomer.getClass().getInterfaces(), handler);
        friendOfBoss.eat();
        friendOfBoss.sleep();
        friendOfBoss.play();

    }
}

